﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.ApplicationPages;
using System.Web.UI;
using System.Data;
using Microsoft.SharePoint.Administration;
using System.Web.UI.WebControls;
using System.ComponentModel;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint;
using System.Web;
using System.Reflection;

namespace zevenseas.TimerJobOverview.ApplicationPages
{
    public class Overview : OperationsPage
    {
        public enum TypeOfAction
        {
            Delete,
            Disable,
            Edit,
            RunNow,
            ChangeSchedule
        }

        public static DataTable JobTable
        {
            get
            {
                DataTable dataTable = new DataTable();
                dataTable.Columns.Add("JobGuid");
                dataTable.Columns.Add("IsDisabled");
                dataTable.Columns.Add("JobTitle");
                dataTable.Columns.Add("LastRunTime");
                dataTable.Columns.Add("Status");
                dataTable.Columns.Add("Schedule");
                dataTable.Columns.Add("StartTime");
                dataTable.Columns.Add("Process");
                dataTable.Columns.Add("Parent");

                return dataTable;
            }
        }

        protected ServiceSelector serviceSelector;
        protected WebApplicationSelector webApplicationSelector;
        protected SPGridView gridViewDefinitions;
        protected ToolBarButton RunNow;
        protected Microsoft.SharePoint.WebControls.Menu MenuViewSelector;
       
        private JobSelectionMode? m_currentView = null;
        
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            switch (this.CurrentView)
            {
                case JobSelectionMode.Farm:
                    this.MenuViewSelector.Text = "All";
                    return;

                case JobSelectionMode.Service:
                    this.MenuViewSelector.Text = "Service";
                    RunNow.Visible = false;
                    return;

                case JobSelectionMode.WebApplication:
                    this.MenuViewSelector.Text = "WebApplication";
                    return;
            }
        }
        
        protected override void Render(System.Web.UI.HtmlTextWriter writer)
        {
            this.webApplicationSelector.Visible = this.CurrentView == JobSelectionMode.WebApplication;
            this.serviceSelector.Visible = this.CurrentView == JobSelectionMode.Service;

            BindData();
            base.Render(writer);
        }
    
        public void DisableLink(object sender, EventArgs e)
        {
            ProcessData(TypeOfAction.Disable);
        }

        public void DeleteLink(object sender, EventArgs e)
        {
            ProcessData(TypeOfAction.Delete);
        }

        public void EditLink(object sender, EventArgs e)
        {
            ProcessData(TypeOfAction.Edit);
        }

        public void RunNowLink(object sender, EventArgs e)
        {
            ProcessData(TypeOfAction.RunNow);
        }

        private string[] RetrieveIDs()
        {
            string[] guids = null;
            if (((base.Request != null) && (base.Request["ctl00$PlaceHolderMain$HiddenSiteSelections"] != null)) && !string.IsNullOrEmpty(base.Request["ctl00$PlaceHolderMain$HiddenSiteSelections"]))
            {
                guids = base.Request["ctl00$PlaceHolderMain$HiddenSiteSelections"].ToString().Split(new Char[] { '#' });
            }
            return guids;
        }

        public void ProcessData(TypeOfAction action)
        {
            SPWebApplication webApplication = SPAdministrationWebApplication.Local;

            string[] guids = null;
            guids = RetrieveIDs();

            if (guids != null)
            {
                foreach (string guid in guids)
                {
                    if (!string.IsNullOrEmpty(guid))
                    {
                        SPJobDefinition jobDefinition = webApplication.JobDefinitions[new Guid(guid)];
                        switch (action)
                        {
                            case TypeOfAction.Delete:
                                jobDefinition.Delete();
                                jobDefinition.Uncache();
                                jobDefinition.Unprovision();
                                break;
                            case TypeOfAction.Disable:
                                jobDefinition.IsDisabled = !jobDefinition.IsDisabled;
                                jobDefinition.Update();
                                break;
                            case TypeOfAction.Edit:
                                SPUtility.Redirect("/_admin/zevenseas.TimerJobOverview/edittimerjob.aspx", SPRedirectFlags.Default, this.Context, "&timerjobid=" + jobDefinition.Id + "&View="+ this.CurrentView);
                                break;

                            case TypeOfAction.RunNow:
                                SPJobDefinition currentJob = null;
                                switch (this.CurrentView)
                                {
                                    case JobSelectionMode.WebApplication:
                                        currentJob = InstantiateJob(
                                        jobDefinition.GetType(),
                                        jobDefinition.Name,                                    
                                        jobDefinition.WebApplication,
                                        jobDefinition.Server,
                                        jobDefinition.LockType
                                            );
                                        break;
                                    case JobSelectionMode.Service:
                                        //Work in progess this one :)
                                        ////currentJob = InstantiateJob(
                                        //jobDefinition.GetType(),
                                        //jobDefinition.Name,
                                        //jobDefinition.Service,
                                        //jobDefinition.Server,
                                        //jobDefinition.LockType
                                        //    );
                                        break;                                   
                                }

                                currentJob.Title = jobDefinition.Title + "-OneTime";
                                currentJob.Name = jobDefinition.Name + "-OneTime";
                                currentJob.Schedule = new SPOneTimeSchedule(DateTime.Now.AddMinutes(5));
                                currentJob.Update();

                                break;                           
                        }
                    }
                }
            }
        }        

        private void BindData()
        {
            List<SPJobDefinitionCollection> definitionCollection = new List<SPJobDefinitionCollection>();

            switch (this.CurrentView)
            {
                case JobSelectionMode.Farm:
                    this.MenuViewSelector.Text = "All";
                    definitionCollection = GetAllJobs();
                    break;

                case JobSelectionMode.Service:
                    this.MenuViewSelector.Text = "Service";
                    if (serviceSelector.CurrentItem != null)
                    {
                        definitionCollection.Add(serviceSelector.CurrentItem.JobDefinitions);
                    }
                    break;

                case JobSelectionMode.WebApplication:
                    this.MenuViewSelector.Text = "WebApplication";
                    definitionCollection.Add(webApplicationSelector.CurrentItem.JobDefinitions);
                    break;
            }
                        
            DataTable definitionJobsTable = JobTable;

            if (definitionCollection != null)
            {
                foreach (SPJobDefinitionCollection jobs in definitionCollection)
                {
                    foreach (SPJobDefinition job in jobs)
                    {
                        DataRow row = definitionJobsTable.NewRow();
                        row["JobGuid"] = job.Id;
                        row["JobTitle"] = job.Title;
                        row["Schedule"] = (job.Schedule.GetType() != typeof(SPOneTimeSchedule)) ? 
                            job.Schedule.ToString() : row["Schedule"] = "One-Time";                       
                        row["IsDisabled"] = job.IsDisabled.ToString();
                        row["LastRunTime"] = (job.LastRunTime.Equals(DateTime.MinValue)) ? 
                            "Never ran" : job.LastRunTime.ToShortDateString() + " - " + job.LastRunTime.ToShortTimeString();
                        
                        row["Parent"] = job.Parent.Name.ToString();
                        definitionJobsTable.Rows.Add(row);

                        foreach (SPServer server in webApplicationSelector.CurrentItem.Farm.Servers)
                        {
                            SPRunningJob runningJob = GetRunningJobStatus(job.Id, server.Name, job.Parent);
                            if (runningJob != null)
                            {
                                row["Status"] += server.Name + " : " + runningJob.Status.ToString() + "<BR>";
                            }
                        }
                    }
                }

                DataView dataView = new DataView(definitionJobsTable);
                dataView.Sort = "JobTitle, Parent";

                gridViewDefinitions.DataSource = dataView.ToTable();
                gridViewDefinitions.DataBind();               
            }
        }

        private SPRunningJob GetRunningJobStatus(Guid guid, string serverName, SPPersistedObject parent)
        {           
            SPRunningJob runningJob = null;

            SPService service = parent as SPService;            
            SPWebService webService = parent as SPWebService;
            SPWebApplication application = parent as SPWebApplication;

            SPRunningJobCollection collection = service != null ? service.RunningJobs : application.RunningJobs;
            
            try
            {
                runningJob = collection.GetInstance(guid, serverName);           
            }
            catch(Exception)
            {}

            return runningJob;
        }
                
        protected JobSelectionMode CurrentView
        {
            get
            {
                if (!this.m_currentView.HasValue)
                {
                    string str = base.Request.QueryString["View"];
                    if (string.IsNullOrEmpty(str))
                    {
                        this.m_currentView = 0;
                    }
                    else
                    {
                        this.m_currentView = (JobSelectionMode?)Enum.Parse(typeof(JobSelectionMode), str, true);
                    }
                }
                return this.m_currentView.Value;
            }
        }

        private List<SPJobDefinitionCollection> GetAllJobs()
        {
            List<SPJobDefinitionCollection> jobCollection = new List<SPJobDefinitionCollection>();
            
            foreach (SPService service in SPFarm.Local.Services)
            {
                if (service.JobDefinitions != null)
                {
                    jobCollection.Add(service.JobDefinitions);
                }

                SPWebService service2 = service as SPWebService;
                if (service2 != null)
                {
                    foreach (SPWebApplication application in service2.WebApplications)
                    {
                        if (application.JobDefinitions != null)
                        {
                            jobCollection.Add(application.JobDefinitions);
                        }
                    }
                }
            }
            return jobCollection;
        }

        SPJobDefinition InstantiateJob(Type type, string jobName, SPWebApplication webApp, SPServer server, SPJobLockType lockType)
        {          
            if (type == null) throw new ApplicationException("Class not found");            
            ConstructorInfo[] constructors = type.GetConstructors();
            SPJobDefinition jobdef = null;

            switch (constructors[1].GetParameters().Length)
            {
                //For SPWebApplication instance
                case 1:
                    jobdef = (SPJobDefinition)constructors[1].Invoke(new object[] { webApp });
                    break;
                //For SPWebAPplication, SPServer and SPJobLockType instances
                case 4:
                    jobdef = (SPJobDefinition)constructors[1].Invoke(new object[] { jobName, webApp, server, lockType });
                    break;
            }
           
            return jobdef;
        }

        SPJobDefinition InstantiateJob(Type type, string jobName, SPService service, SPServer server, SPJobLockType lockType)
        {
            //Type type = Type.GetType(jobClassName);
            if (type == null) throw new ApplicationException("Class not found");
            ConstructorInfo[] constructors = type.GetConstructors();
            SPJobDefinition jobdef = null;

            switch (constructors[1].GetParameters().Length)
            {
                //For SPWebApplication instance
                case 1:
                    jobdef = (SPJobDefinition)constructors[1].Invoke(new object[] { service });
                    break;
                //For SPWebAPplication, SPServer and SPJobLockType instances
                case 4:
                    jobdef = (SPJobDefinition)constructors[1].Invoke(new object[] { jobName, service, server, lockType });
                    break;
            }
                        
            return jobdef;
        }
    }

    public class SPBoundHTMLField : SPBoundField
    {
        protected override void ChildControlDataBinding(Control childControl, object dataItem,
                                                        MemberDescriptor dataFieldPropertyDescriptor)
        {
            PlaceHolder placeHolder = (PlaceHolder)childControl;
            string propertyValueAsHtml = GetPropertyValueAsHtml(dataItem, dataFieldPropertyDescriptor.Name);
            if (!string.IsNullOrEmpty(propertyValueAsHtml))
            {
                Label label = new Label();
                label.Text = SPHttpUtility.HtmlDecode(propertyValueAsHtml);
                placeHolder.Controls.Add(label);
            }
        }
        protected override Control GetChildControlInstance()
        {
            return new PlaceHolder();
        }
    }
}
